package com.lzt.flow.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lzt.flow.entity.Node;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * 解析logicflow
 *
 * @author lizhitao
 */
@Slf4j
public class LfParse {

    private static final String MERGE_NODE = "dataMerge";
    private static final String START_NODE = "startNode";
    private static List<String> excludeList = new ArrayList<>(Arrays.asList("startNode", "dataReport"));


    public static String buildEL(Node node, boolean appendProperties) {
        StringBuilder elSb = new StringBuilder();
        elSb.append("THEN(");
        traverseNode(node, elSb, appendProperties);
        elSb.append(")");
        return elSb.toString();
    }

    /**
     * 构建node树
     *
     * @param nodeStr
     * @param nodeId
     * @return
     */
    public static Node buildNode(String nodeStr, String nodeId) {
        JSONObject rootJson = JSON.parseObject(nodeStr);
        List<Node> nodeList = rootJson.getJSONArray("nodes").toJavaList(Node.class);
        List<Node> edgeList = rootJson.getJSONArray("edges").toJavaList(Node.class);

        Node startNode = getStartNode(nodeList, edgeList);
        if (nodeId != null) {
            Node endNode = getNodeById(nodeList, nodeId);
            getNextNode(nodeList, edgeList, startNode, endNode);
            pruneNode(startNode, endNode);
        } else {
            getNextNode(nodeList, edgeList, startNode, null);
        }
        return startNode;
    }

    /**
     * 递归遍历节点
     *
     * @param curNode
     * @param elStr
     */
    private static void traverseNode(Node curNode, StringBuilder elStr, boolean appendProperties) {
        List<Node> nextNodeList = curNode.getNextNodeList();
        if (nextNodeList.size() == 1) {
            Node nextNode = nextNodeList.get(0);
            if (MERGE_NODE.equals(nextNode.getType())) {
                appendNode(elStr, curNode, appendProperties);
            } else {
                appendNode(elStr, curNode, appendProperties).append(",");
                traverseNode(nextNode, elStr, appendProperties);
            }
        } else if (nextNodeList.size() > 1) {
            appendNode(elStr, curNode, appendProperties).append(",");
            elStr.append("WHEN(");
            Node mergeNode = null;
            for (Node node : nextNodeList) {
                if (mergeNode == null) {
                    mergeNode = getMergeNode(node);
                }
            }
            if (mergeNode == null) {
                throw new RuntimeException(curNode.getType() + "未找到流程合并节点");
            }
            for (int i = 0; i < nextNodeList.size(); i++) {
                Node node = nextNodeList.get(i);
                if (node.getId().equals(mergeNode.getId())) {
                    continue;
                }
                if (MERGE_NODE.equals(node.getNextNodeList().get(0).getType())) {
                    appendNode(elStr, node, appendProperties);
                } else {
                    elStr.append("THEN(");
                    traverseNode(node, elStr, appendProperties);
                    elStr.append(")");
                }
                if (i != nextNodeList.size() - 1 && !nextNodeList.get(i + 1).getId().equals(mergeNode.getId())) {
                    elStr.append(",");
                }
            }
            elStr.append("),");
            traverseNode(mergeNode, elStr, appendProperties);
        } else {
            appendNode(elStr, curNode, appendProperties);
        }
    }


    /**
     * 组装树节点
     *
     * @param nodeList
     * @param edgeList
     * @param startNode
     * @param endNode
     */
    private static void getNextNode(List<Node> nodeList, List<Node> edgeList, Node startNode, Node endNode) {
        List<Node> allNextNodeList = new ArrayList<>();
        startNode.setNextNodeList(allNextNodeList);
        for (Node node : edgeList) {
            if (node.getSourceNodeId().equals(startNode.getId())) {
                Node nextNode = getNodeById(nodeList, node.getTargetNodeId());
                getPreNodeList(nodeList, edgeList, nextNode);
                nextNode.setNextNodeList(new ArrayList<>());
                allNextNodeList.add(nextNode);
            }
        }
        if (allNextNodeList.isEmpty()) {
            return;
        }
        for (Node node : allNextNodeList) {
            if (endNode != null && node.getId().equals(endNode.getId())) {
                return;
            }
            getNextNode(nodeList, edgeList, node, endNode);
        }
    }

    /**
     * 只维护上一层级
     *
     * @param nodeList
     * @param edgeList
     * @param paramNode
     */
    private static void getPreNodeList(List<Node> nodeList, List<Node> edgeList, Node paramNode) {
        List<Node> preNodeList = new ArrayList<>();
        for (Node node : edgeList) {
            if (node.getTargetNodeId().equals(paramNode.getId())) {
                Node preNode = getNodeById(nodeList, node.getSourceNodeId());
                Node targetNode = new Node();
                BeanUtils.copyProperties(preNode, targetNode, "preNodeList", "nextNodeList");
                preNodeList.add(targetNode);
            }
        }
        paramNode.setPreNodeList(preNodeList);
    }

    /**
     * 获取合并节点
     *
     * @param curNode
     * @return
     */
    private static Node getMergeNode(Node curNode) {
        Node nextNode = curNode;
        while (true) {
            if (MERGE_NODE.equals(nextNode.getType())) {
                return nextNode;
            }
            if (nextNode.getNextNodeList().size() != 1) {
                break;
            }
            nextNode = nextNode.getNextNodeList().get(0);
        }
        return null;
    }

    /**
     * 修剪节点
     *
     * @param startNode
     * @param endNode
     * @return
     */
    private static boolean pruneNode(Node startNode, Node endNode) {
        if (startNode == null || endNode == null || startNode.getNextNodeList().size() == 0) {
            return false;
        }
        boolean find = false;
        Iterator<Node> iterator = startNode.getNextNodeList().iterator();
        while (iterator.hasNext()) {
            Node nextNode = iterator.next();
            if (nextNode.getId().equals(endNode.getId())) {
                find = true;
                continue;
            }
            boolean exist = pruneNode(nextNode, endNode);
            if (!exist) {
                iterator.remove();
            }
        }
        // 最后一层
        if (find) {
            return true;
        }
        // 上一层
        if (startNode.getNextNodeList().size() > 0) {
            return true;
        }
        return false;
    }

    /**
     * 获取开始节点
     *
     * @param nodeList
     * @return
     */
    private static Node getStartNode(List<Node> nodeList, List<Node> edgeList) {
        for (Node node : nodeList) {
            if (START_NODE.equals(node.getType()) && edgeList.stream().noneMatch(v1 -> v1.getTargetNodeId().equals(node.getId()))) {
                return node;
            }
        }
        throw new RuntimeException("未找到流程开始节点");
    }

    /**
     * 通过id获取node
     *
     * @param nodeList
     * @param id
     * @return
     */
    private static Node getNodeById(List<Node> nodeList, String id) {
        return nodeList.stream().filter(v -> id.equals(v.getId())).findFirst().get();
    }

    private static StringBuilder appendNode(StringBuilder elStr, Node node, boolean appendProperties) {
        node.getProperties().put("nodeId", node.getId());
        if (appendProperties) {
            elStr.append(node.getType()).append(".data('").append(node.getProperties()).append("')");
        } else {
            elStr.append(node.getType());
        }
        return elStr;
    }
}
